import FluidTerrainProvider from "./FluidTerrainProvider"
import Cesium from '../Ces/Cesium'
import FluidPrimitive from "./FluidPrimitive"
import FluidParameter from "./FluidParameter"

class FluidManager {
  private _fluidTerrainProvider: FluidTerrainProvider
  private fluidTiles = new Cesium.AssociativeArray()
  fluidParameter = new FluidParameter()
  constructor() {
  }

  get fluidTerrainProvider() {
    return this._fluidTerrainProvider
  }

  set fluidTerrainProvider(value: FluidTerrainProvider) {
    this._fluidTerrainProvider = value
  }

  getTile(z: number, y: number, x: number): FluidPrimitive {
    const tileId = JSON.stringify({ z, y, x })
    return this.fluidTiles.get(tileId)
  }

  addTile(z: number, y: number, x: number) {
    const tileId = JSON.stringify({ z, y, x })
    const fluidTiles = this.fluidTiles
    let fluidPrimitive = fluidTiles.get(tileId)
    if (fluidPrimitive) return Promise.resolve(fluidPrimitive)
    return this.fluidTerrainProvider.requestTerrain(z, y, x).then((terrainImage) => {
      fluidPrimitive = new FluidPrimitive({
        terrainImage,
        fluidParameter: this.fluidParameter
      })

      const westTile = this.getTile(z, y, x - 1)
      if (westTile) {
        fluidPrimitive.fluidNeighbor.W = westTile
        westTile.fluidNeighbor.E = fluidPrimitive
      }

      const southTile = this.getTile(z, y + 1, x)
      if (southTile) {
        fluidPrimitive.fluidNeighbor.S = southTile
        southTile.fluidNeighbor.N = fluidPrimitive
      }

      const eastTile = this.getTile(z, y, x + 1)
      if (eastTile) {
        fluidPrimitive.fluidNeighbor.E = eastTile
        eastTile.fluidNeighbor.W = fluidPrimitive
      }

      const northTile = this.getTile(z, y - 1, x)
      if (northTile) {
        fluidPrimitive.fluidNeighbor.N = northTile
        northTile.fluidNeighbor.S = fluidPrimitive
      }

      fluidTiles.set(tileId, fluidPrimitive)
      fluidPrimitive.destroyForCache = fluidPrimitive.destroy
      fluidPrimitive.destroy = () => {
        fluidPrimitive.destroyForCache()
        fluidTiles.remove(tileId)
      }
      return fluidPrimitive
    })
  }

  update(frameState) {
    if (!frameState.passes.render) return
    for (let i = 0; i < this.fluidTiles.length; i++) {
      const p = this.fluidTiles._array[i]
      p.update(frameState)
    }
  }

  removeAll() {
    const fluidTiles = this.fluidTiles
    const myArray = [].concat(fluidTiles._array)
    myArray.forEach(element => {
      element.destroy()
    })
  }

  destroy() {
    this.removeAll()
    this.fluidParameter = this.fluidParameter && this.fluidParameter.destroy()
    return Cesium.destroyObject(this)
  }

  isDestroyed() {
    return false
  }
}

export default FluidManager
